Trò chơi đua xe động vật trong UNITY Engine
- ImportTiled2Unity.Mesh.cs
- Editor /
- Scripts /
- Tiled2Unity /
- Assets /
- project /
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Xml;
6 using System.Xml.Linq;
7
8 using UnityEditor;
9 using UnityEngine;
10
11 namespace Tiled2Unity
12 {
13 // Handles a Mesh being imported.
14 // At this point we should have everything we need to build out any prefabs for the tiled map object
15 partial class ImportTiled2Unity
16 {
17 public void MeshImported(string objPath)
18 {
19 string xmlPath = ImportUtils.GetXmlPath(objPath);
20 XDocument doc = XDocument.Load(xmlPath);
21 foreach (var xmlPrefab in doc.Root.Elements("Prefab"))
22 {
23 CreatePrefab(xmlPrefab, objPath);
24 }
25 }
26
27 private void CreatePrefab(XElement xmlPrefab, string objPath)
28 {
29 var customImporters = GetCustomImporterInstances();
30
31 // Part 1: Create the prefab
32 string prefabName = xmlPrefab.Attribute("name").Value;
33 float prefabScale = ImportUtils.GetAttributeAsFloat(xmlPrefab, "scale", 1.0f);
34 GameObject tempPrefab = new GameObject(prefabName);
35 HandleCustomProperties(tempPrefab, xmlPrefab, customImporters);
36
37 // Part 2: Build out the prefab
38 AddGameObjectsTo(tempPrefab, xmlPrefab, objPath, customImporters);
39
40 // Part 3: Allow for customization from other editor scripts to be made on the prefab
41 // (These are generally for game-specific needs)
42 CustomizePrefab(tempPrefab, customImporters);
43
44 // Part 3.5: Apply the scale only after all children have been added
45 tempPrefab.transform.localScale = new Vector3(prefabScale, prefabScale, prefabScale);
46
47 // Part 4: Save the prefab, keeping references intact.
48 string prefabPath = ImportUtils.GetPrefabPath(prefabName);
49 UnityEngine.Object finalPrefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject));
50
51 if (finalPrefab == null)
52 {
53 // The prefab needs to be created
54 ImportUtils.ReadyToWrite(prefabPath);
55 finalPrefab = PrefabUtility.CreateEmptyPrefab(prefabPath);
56 }
57
58 // Replace the prefab, keeping connections based on name.
59 PrefabUtility.ReplacePrefab(tempPrefab, finalPrefab, ReplacePrefabOptions.ReplaceNameBased);
60
61 // Destroy the instance from the current scene hiearchy.
62 UnityEngine.Object.DestroyImmediate(tempPrefab);
63 }
64
65 private void AddGameObjectsTo(GameObject parent, XElement xml, string objPath, IList<ICustomTiledImporter> customImporters)
66 {
67 foreach (XElement goXml in xml.Elements("GameObject"))
68 {
69 string name = ImportUtils.GetAttributeAsString(goXml, "name", "");
70 string copyFrom = ImportUtils.GetAttributeAsString(goXml, "copy", "");
71
72 GameObject child = null;
73 if (!String.IsNullOrEmpty(copyFrom))
74 {
75 child = CreateCopyFromMeshObj(copyFrom, objPath);
76 if (child == null)
77 {
78 // We're in trouble. Errors should already be in the log.
79 return;
80 }
81 }
82 else
83 {
84 child = new GameObject();
85 }
86
87 if (!String.IsNullOrEmpty(name))
88 {
89 child.name = name;
90 }
91
92 float x = ImportUtils.GetAttributeAsFloat(goXml, "x", 0);
93 float y = ImportUtils.GetAttributeAsFloat(goXml, "y", 0);
94 child.transform.position = new Vector3(x, y, 0);
95
96 // Assign the child to the parent
97 child.transform.parent = parent.transform;
98
99 // Do we have any collision data
100 AddCollidersTo(child, goXml);
101
102 // Do we have any children of our own?
103 AddGameObjectsTo(child, goXml, objPath, customImporters);
104
105 // Does this game object have a tag?
106 AssignTagTo(child, goXml);
107
108 // Does this game object have a layer?
109 AssignLayerTo(child, goXml);
110
111 // Are there any custom properties?
112 HandleCustomProperties(child, goXml, customImporters);
113 }
114 }
115
116 private void AssignLayerTo(GameObject gameObject, XElement xml)
117 {
118 string layerName = ImportUtils.GetAttributeAsString(xml, "layer", "");
119 if (String.IsNullOrEmpty(layerName))
120 return;
121
122 int layerId = LayerMask.NameToLayer(layerName);
123 if (layerId == -1)
124 {
125 string msg = String.Format("Layer '{0}' is not defined for '{1}'. Check project settings in Edit->Project Settings->Tags & Layers",
126 layerName,
127 GetFullGameObjectName(gameObject.transform));
128 Debug.LogError(msg);
129 return;
130 }
131
132 // Set the layer on ourselves (and our children)
133 AssignLayerIdTo(gameObject, layerId);
134 }
135
136 private void AssignLayerIdTo(GameObject gameObject, int layerId)
137 {
138 if (gameObject == null)
139 return;
140
141 gameObject.layer = layerId;
142
143 foreach (Transform child in gameObject.transform)
144 {
145 if (child.gameObject == null)
146 continue;
147
148 // Do not set the layerId on a child that has already had his layerId explicitly set
149 if (child.gameObject.layer != 0)
150 continue;
151
152 AssignLayerIdTo(child.gameObject, layerId);
153 }
154 }
155
156 private void AssignTagTo(GameObject gameObject, XElement xml)
157 {
158 string tag = ImportUtils.GetAttributeAsString(xml, "tag", "");
159 if (String.IsNullOrEmpty(tag))
160 return;
161
162 // Let the user know if the tag doesn't exist in our project sttings
163 try
164 {
165 gameObject.tag = tag;
166 }
167 catch (UnityException)
168 {
169 string msg = String.Format("Tag '{0}' is not defined for '{1}'. Check project settings in Edit->Project Settings->Tags & Layers",
170 tag,
171 GetFullGameObjectName(gameObject.transform));
172 Debug.LogError(msg);
173 }
174 }
175
176 private string GetFullGameObjectName(Transform xform)
177 {
178 if (xform == null)
179 return "";
180 string parentName = GetFullGameObjectName(xform.parent);
181
182 if (String.IsNullOrEmpty(parentName))
183 return xform.name;
184
185 return String.Format("{0}/{1}", parentName, xform.name);
186 }
187
188 private void AddCollidersTo(GameObject gameObject, XElement xml)
189 {
190 // Box colliders
191 foreach (XElement xmlBoxCollider2D in xml.Elements("BoxCollider2D"))
192 {
193 BoxCollider2D collider = gameObject.AddComponent<BoxCollider2D>();
194 float width = ImportUtils.GetAttributeAsFloat(xmlBoxCollider2D, "width");
195 float height = ImportUtils.GetAttributeAsFloat(xmlBoxCollider2D, "height");
196 collider.size = new Vector2(width, height);
197 collider.offset = new Vector2(width * 0.5f, -height * 0.5f);
198 }
199
200 // Circle colliders
201 foreach (XElement xmlCircleCollider2D in xml.Elements("CircleCollider2D"))
202 {
203 CircleCollider2D collider = gameObject.AddComponent<CircleCollider2D>();
204 float radius = ImportUtils.GetAttributeAsFloat(xmlCircleCollider2D, "radius");
205 collider.radius = radius;
206 collider.offset = new Vector2(radius, -radius);
207 }
208
209 // Edge colliders
210 foreach (XElement xmlEdgeCollider2D in xml.Elements("EdgeCollider2D"))
211 {
212 EdgeCollider2D collider = gameObject.AddComponent<EdgeCollider2D>();
213 string data = xmlEdgeCollider2D.Element("Points").Value;
214
215 // The data looks like this:
216 // x0,y0 x1,y1 x2,y2 ...
217 var points = from pt in data.Split(' ')
218 let x = Convert.ToSingle(pt.Split(',')[0])
219 let y = Convert.ToSingle(pt.Split(',')[1])
220 select new Vector2(x, y);
221
222 collider.points = points.ToArray();
223
224 }
225
226 // Polygon colliders
227 foreach (XElement xmlPolygonCollider2D in xml.Elements("PolygonCollider2D"))
228 {
229 PolygonCollider2D collider = gameObject.AddComponent<PolygonCollider2D>();
230
231 var paths = xmlPolygonCollider2D.Elements("Path").ToArray();
232 collider.pathCount = paths.Count();
233
234 for (int p = 0; p < collider.pathCount; ++p)
235 {
236 string data = paths[p].Value;
237
238 // The data looks like this:
239 // x0,y0 x1,y1 x2,y2 ...
240 var points = from pt in data.Split(' ')
241 let x = Convert.ToSingle(pt.Split(',')[0])
242 let y = Convert.ToSingle(pt.Split(',')[1])
243 select new Vector2(x, y);
244
245 collider.SetPath(p, points.ToArray());
246 }
247 }
248 }
249
250 private GameObject CreateCopyFromMeshObj(string copyFromName, string objPath)
251 {
252 // Find a matching game object within the mesh object and "copy" it
253 // (In Unity terms, the Instantiated object is a copy)
254 UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(objPath);
255 foreach (var obj in objects)
256 {
257 if (obj.name != copyFromName)
258 continue;
259
260 // We have a match but is it a game object?
261 GameObject gameObj = GameObject.Instantiate(obj) as GameObject;
262 if (gameObj == null)
263 continue;
264
265 // Reset the name so it is not decorated by the Instantiate call
266 gameObj.name = obj.name;
267 return gameObj;
268 }
269
270 // If we're here then there's an error with the mesh name
271 Debug.LogError(String.Format("No mesh named '{0}' to copy from.", copyFromName));
272 return null;
273 }
274
275 private void HandleCustomProperties(GameObject gameObject, XElement goXml, IList<ICustomTiledImporter> importers)
276 {
277 var props = from p in goXml.Elements("Property")
278 select new { Name = p.Attribute("name").Value, Value = p.Attribute("value").Value };
279
280 if (props.Count() > 0)
281 {
282 var dictionary = props.OrderBy(p => p.Name).ToDictionary(p => p.Name, p => p.Value);
283 foreach (ICustomTiledImporter importer in importers)
284 {
285 importer.HandleCustomProperties(gameObject, dictionary);
286 }
287 }
288 }
289
290 private void CustomizePrefab(GameObject prefab, IList<ICustomTiledImporter> importers)
291 {
292 foreach (ICustomTiledImporter importer in importers)
293 {
294 importer.CustomizePrefab(prefab);
295 }
296 }
297
298 private IList<ICustomTiledImporter> GetCustomImporterInstances()
299 {
300 // Report an error for ICustomTiledImporter classes that don't have the CustomTiledImporterAttribute
301 var errorTypes = from a in AppDomain.CurrentDomain.GetAssemblies()
302 from t in a.GetTypes()
303 where typeof(ICustomTiledImporter).IsAssignableFrom(t)
304 where !t.IsAbstract
305 where Attribute.GetCustomAttribute(t, typeof(CustomTiledImporterAttribute)) == null
306 select t;
307 foreach (var t in errorTypes)
308 {
309 Debug.LogError(String.Format("ICustomTiledImporter type '{0}' is missing CustomTiledImporterAttribute", t));
310 }
311
312 // Find all the types with the CustomTiledImporterAttribute, instantiate them, and give them a chance to customize our prefab
313 var types = from a in AppDomain.CurrentDomain.GetAssemblies()
314 from t in a.GetTypes()
315 where typeof(ICustomTiledImporter).IsAssignableFrom(t)
316 where !t.IsAbstract
317 from attr in Attribute.GetCustomAttributes(t, typeof(CustomTiledImporterAttribute))
318 let custom = attr as CustomTiledImporterAttribute
319 orderby custom.Order
320 select t;
321
322 var instances = types.Select(t => (ICustomTiledImporter)Activator.CreateInstance(t));
323 return instances.ToList();
324 }
325
326 } // end class
327 } // end namespace
Handles a Mesh being imported.
At this point we should have everything we need to build out any prefabs for the tiled map object
Part 1: Create the prefab
Part 2: Build out the prefab
Part 3: Allow for customization from other editor scripts to be made on the prefab
(These are generally for game-specific needs)
Part 3.5: Apply the scale only after all children have been added
Part 4: Save the prefab, keeping references intact.
The prefab needs to be created
Replace the prefab, keeping connections based on name.
Destroy the instance from the current scene hiearchy.
We're in trouble. Errors should already be in the log.
Assign the child to the parent
Do we have any collision data
Do we have any children of our own?
Does this game object have a tag?
Does this game object have a layer?
Are there any custom properties?
Set the layer on ourselves (and our children)
Do not set the layerId on a child that has already had his layerId explicitly set
Let the user know if the tag doesn't exist in our project sttings
Box colliders
Circle colliders
Edge colliders
The data looks like this:
x0,y0 x1,y1 x2,y2 ...
Polygon colliders
The data looks like this:
x0,y0 x1,y1 x2,y2 ...
Find a matching game object within the mesh object and "copy" it
(In Unity terms, the Instantiated object is a copy)
We have a match but is it a game object?
Reset the name so it is not decorated by the Instantiate call
If we're here then there's an error with the mesh name
Report an error for ICustomTiledImporter classes that don't have the CustomTiledImporterAttribute
Find all the types with the CustomTiledImporterAttribute, instantiate them, and give them a chance to customize our prefab
} end class
} end namespace